package com.qianfeng.smsplatform.gateway.filters;

//
//                            _ooOoo_  
//                           o8888888o  
//                           88" . "88  
//                           (| -_- |)  
//                            O\ = /O  
//                        ____/`---'\____  
//                      .   ' \\| |// `.  
//                       / \\||| : |||// \  
//                     / _||||| -:- |||||- \  
//                       | | \\\ - /// | |  
//                     | \_| ''\---/'' | |  
//                      \ .-\__ `-` ___/-. /  
//                   ___`. .' /--.--\ `. . __  
//                ."" '< `.___\_<|>_/___.' >'"".  
//               | | : `- \`.;`\ _ /`;.`/ - ` : | |  
//                 \ \ `-. \_ __\ /__ _/ .-` / /  
//         ======`-.____`-.___\_____/___.-`____.-'======  
//                            `=---='  
//  
//         .............................................  
//                  佛祖镇楼                  BUG辟易  
//          佛曰:  
//                  写字楼里写字间，写字间里程序员；  
//                  程序人员写程序，又拿程序换酒钱。  
//                  酒醒只在网上坐，酒醉还来网下眠；  
//                  酒醉酒醒日复日，网上网下年复年。  
//                  但愿老死电脑间，不愿鞠躬老板前；  
//                  奔驰宝马贵者趣，公交自行程序员。  
//                  别人笑我忒疯癫，我笑自己命太贱；  
//  


import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import com.netflix.zuul.exception.ZuulException;
import com.qianfeng.smsplatform.gateway.cache.ApiMappingCache;
import com.qianfeng.smsplatform.gateway.cache.ApiMappingFilterCache;
import com.qianfeng.smsplatform.gateway.cache.GrayReleaseCache;
import com.qianfeng.smsplatform.gateway.pojo.TApiMapping;
import com.qianfeng.smsplatform.gateway.pojo.TGrayReleaseConfig;
import io.jmnarloch.spring.cloud.ribbon.support.RibbonFilterContextHolder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.netflix.zuul.filters.support.FilterConstants;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletRequest;
import java.util.*;

/**
 * Created by Jackiechan on 2022/2/14/16:38
 *
 * @author Jackiechan
 * @version 1.0
 * @since 1.0
 */
@Component
public class GrayReleaseFilter extends ZuulFilter {
    private ApiMappingCache apiMappingCache;

    private ApiMappingFilterCache apiMappingFilterCache;


    @Autowired
    public void setApiMappingFilterCache(ApiMappingFilterCache apiMappingFilterCache) {
        this.apiMappingFilterCache = apiMappingFilterCache;
    }
    private HashMap<String, List<String>> grayReleaseMap = new HashMap<>();

    @Autowired
    public void setApiMappingCache(ApiMappingCache apiMappingCache) {
        this.apiMappingCache = apiMappingCache;
    }

    private GrayReleaseCache grayReleaseCache;

    @Autowired
    public void setGrayReleaseCache(GrayReleaseCache grayReleaseCache) {
        this.grayReleaseCache = grayReleaseCache;
    }

    @Override
    public String filterType() {
        return FilterConstants.PRE_TYPE;
    }

    @Override
    public int filterOrder() {
        return FilterConstants.PRE_DECORATION_FILTER_ORDER+100+apiMappingFilterCache.getIndex(this.getClass().getSimpleName().toLowerCase());
    }

    @Override
    public boolean shouldFilter() {
        boolean shouldFilter = apiMappingFilterCache.shouldFilter(this.getClass().getSimpleName().toLowerCase());
        RequestContext currentContext = RequestContext.getCurrentContext();
        boolean zuulResponse = currentContext.sendZuulResponse();//根据前置过滤器的结果来决定是否启用当前过滤器,前面拦截这个就不执行
        if (!zuulResponse||!shouldFilter) {
            return false;
        }
        HttpServletRequest request = currentContext.getRequest();
        String method = request.getParameter("method");//传递的请求的标识,这个标识在内部对应一个真正的服务和id,好处是如果要变化或者要停止,那么直接修改这个标识对应的服务就行了,不需要更新客户端
        TApiMapping apiMapping = apiMappingCache.get(method);//查找api网关
        //获取当前服务的灰度发布配置
        Set<TGrayReleaseConfig> grayReleaseConfigSet = grayReleaseCache.get(apiMapping.getServiceId() + apiMapping.getInsideApiUrl());
        currentContext.put("gray", grayReleaseConfigSet);//临时缓存当前数据
        System.err.println(grayReleaseConfigSet);
        if (grayReleaseConfigSet == null||grayReleaseConfigSet.size()==0) {
            return false;
        }
        //判断下当前请求的地址需要不需要灰度发布,也就是必须请求的服务和地址是处于灰度发布的状态才可以
        //要先根据请求的服务和地址去查询灰度发布保存的数据,查到了说明需要灰度,查不到不需要
        //灰度发布的数据保存在哪里,我们保存在数据库中,但是数据不怎么变化,可以保存到redis,同时还可以保存在本地一份
        //从本地获取就可以了,然后根据访问的服务名和地址区分就行
        //当前访问的服务名和地址是什么?下一个 我们缓存是一个list集合,如何根据服务名和地址查找?
        //经过分析,我们发现,我们的网关都是一个地址来处理所有的请求,通过传递一个不同的参数来进行服务的区分
        //这个技术我们称之为开放平台,也就是我们本地还需要保存一份开放平台的路由映射的信息
        //通过获取用户的传递的method,然后从本地的路由信息中找到这个method对应的服务名字和地址
        //上面的服务的list集合我们遍历转换成一个map,map的key就是服务名字和地址的拼接字符串
        //我们利用我们获取到的服务名和地址拼接一个字符串, 从map中获取数据
        //获取到了就代表着要灰度发布,可以和前面的拦截信息一起进行判断
        //如果没有获取到就代表不需要灰度发布
        return zuulResponse;
    }

    @Override
    public Object run() throws ZuulException {
        RequestContext context = RequestContext.getCurrentContext();
        String method = context.getRequest().getParameter("method");
        List<String> grayList = grayReleaseMap.get(method);//获取当前这个接口的灰度发布参数
        if (grayList == null) {
            grayList = new ArrayList<>();
        }
        if (grayList.size()==0) {
            //如果这个run执行,那么这个set一定有数据
            Set<TGrayReleaseConfig> grayReleaseConfigSet= (Set<TGrayReleaseConfig>) context.get("gray");
            List<String> allGrayString = new ArrayList<>();
            grayReleaseConfigSet.forEach(tGrayReleaseConfig -> {
                for (Integer i = 0; i < tGrayReleaseConfig.getPercent(); i++) {
                    allGrayString.add(tGrayReleaseConfig.getForward());
                }
            });
            System.err.println(allGrayString);
            Collections.shuffle(allGrayString);//打乱顺序
            grayList.addAll(allGrayString);
            grayReleaseMap.put(method, grayList);//保存到本地
        }
        System.err.println(grayList);
        //  HttpServletRequest request = context.getRequest();
       // String huidu = request.getParameter("huidu");
        //区分出到底怎么访问,找到标识,标识基本上不会变化,所以保存到本地一份
        //上面我们已经判断出来了当前请求需要灰度发布,并且数据已经获取到了
        //从保存的数据中招出所有的标识,然后根据我们大约的次数随机挑选一个标识,传递到下面
        RibbonFilterContextHolder.getCurrentContext().add("forward", grayList.remove(0));//添加服务的标识,会自动匹配对应的标识的服务
        return null;
    }
}
